package org.lc.stk.security.jwt;

import java.io.IOException;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * JWT身份认证过滤器
 *
 * <p>该过滤器位于Spring Security过滤器链中，在{@link UsernamePasswordAuthenticationFilter}之前执行，
 * 负责拦截所有HTTP请求并处理其中的JWT令牌。</p>
 *
 * <p>认证流程:</p>
 * <ol>
 *   <li>从请求头中提取JWT令牌</li>
 *   <li>验证令牌的有效性(签名、过期时间等)</li>
 *   <li>从令牌中提取用户信息</li>
 *   <li>加载用户详细信息</li>
 *   <li>创建认证令牌并设置到安全上下文</li>
 * </ol>
 *
 * <p>交互组件:</p>
 * <ul>
 *   <li>{@link JwtUtil}: 处理JWT的创建、解析和验证</li>
 *   <li>{@link UserDetailsService}: 加载用户详细信息</li>
 *   <li>{@link SecurityContextHolder}: 存储认证信息</li>
 * </ul>
 *
 * <p>异常处理:</p>
 * <ul>
 *   <li>令牌不存在: 继续过滤器链,交由其他过滤器处理</li>
 *   <li>令牌无效: 继续过滤器链,交由其他过滤器处理</li>
 *   <li>令牌过期: 继续过滤器链,交由其他过滤器处理</li>
 *   <li>用户不存在: 继续过滤器链,交由其他过滤器处理</li>
 * </ul>
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     * JWT工具类
     *
     * <p>提供JWT令牌的创建、解析和验证功能。包括:</p>
     * <ul>
     *   <li>生成JWT令牌</li>
     *   <li>验证令牌有效性</li>
     *   <li>从令牌中提取信息</li>
     *   <li>检查令牌是否过期</li>
     * </ul>
     */
    private final JwtUtil jwtUtil;

    /**
     * 用户详情服务
     *
     * <p>负责根据用户名加载用户的完整信息，包括:</p>
     * <ul>
     *   <li>用户基本信息</li>
     *   <li>用户权限列表</li>
     *   <li>账号状态信息</li>
     * </ul>
     */
    private final UserDetailsService userDetailsService;

    /**
     * JWT配置属性
     *
     * <p>包含JWT相关的配置信息:</p>
     * <ul>
     *   <li>令牌密钥</li>
     *   <li>令牌有效期</li>
     *   <li>请求头名称</li>
     *   <li>令牌前缀</li>
     * </ul>
     */
    private final JwtProperties jwtProperties;

    /**
     * 构造方法，用于初始化 JWT 工具类、用户详情服务和 JWT 属性配置。
     *
     * @param jwtUtil JWT 工具类实例
     * @param userDetailsService 用户详情服务实例
     * @param jwtProperties JWT 属性配置实例
     */
    public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService, JwtProperties jwtProperties) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
        this.jwtProperties = jwtProperties;
    }

    /**
     * 核心过滤方法，用于处理每个 HTTP 请求，执行 JWT 身份验证逻辑。
     *
     * @param request HTTP 请求对象
     * @param response HTTP 响应对象
     * @param filterChain 过滤器链，用于继续后续过滤器的处理
     * @throws ServletException 如果处理请求时发生 Servlet 异常
     * @throws IOException 如果处理请求时发生 IO 异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // 从 HTTP 请求头中获取授权信息（JWT 令牌）
        final String authorizationHeader = request.getHeader(jwtProperties.getHeader());

        String username = null;
        String jwt = null;

        // 检查授权头是否存在且以 JWT 前缀开头
        if (authorizationHeader != null && authorizationHeader.startsWith(jwtProperties.getPrefix())) {
            // 提取 JWT 令牌，移除前缀"Bearer "
            jwt = authorizationHeader.substring(jwtProperties.getPrefix().length()).trim();
            // 使用 JWT 工具类从令牌中提取用户名
            username = jwtUtil.extractUsername(jwt);
        }

        // 如果用户名不为空且当前安全上下文未进行身份验证
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 通过用户详情服务加载用户信息
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            // 验证 JWT 令牌的有效性
            if (jwtUtil.validateToken(jwt, userDetails)) {
                // 创建身份验证令牌，包含用户详情和权限信息
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                // 设置身份验证令牌的详细信息，包括请求来源等
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // 将身份验证令牌存储到安全上下文中
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        // 继续执行过滤器链中的下一个过滤器
        filterChain.doFilter(request, response);
    }
}